#
# CONVENTIONS
#	place newer tests first (ensures they fail quicker if they fail, and that they clean-up after themselves so the remaining tests can run)
#
# REQUIRED FILES (not provided, should be in same directory as this)
# 	account1.txt
#		single line of text - the account name to sign into ICHC with, followed by the password, separated by :
#      (please start account name with "selenium"  (example "selenium_m0nk_3y:mysecretpassword")
#	account2.txt	
#		single line of text - the account name to sign into ICHC with, followed by the password, separated by :
#      (please start account name with "selenium"  (example "selenium_m0nk_3y:mysecretpassword")
#		this account is used to send offline messages to
#
#

require 'rubygems'
require 'selenium-webdriver'
#require 'test/unit'  #hmm, would be handy to use:)

def text_from_file(filename)
	File.open(filename, 'r') { |f| f.read }
end
	
def sign_into_site(server, driver, file)
	driver.get server
	# sleep(1)
	driver.find_element(:link, "Sign-in").click
	begin
		account_name_field = driver.find_element :name => "txtAccountName"
	rescue
		print "didn't find accountname for signing in, giving it an extra 5 seconds\n"
		sleep 5
		account_name_field = driver.find_element :name => "txtAccountName"
	end
	account = text_from_file(file)
	account_name_field.send_keys account.split(":")[0]
	account_password_field = driver.find_element :name => "txtPassword1"
	account_password_field.send_keys account.split(":")[1]
	signin_button = driver.find_element :id => "btnSubmit"
	signin_button.click
end
	
def sign_out_of_site(server, driver)
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	element = wait.until { driver.find_element(:link_text => "signout") }
	element.click
end
	
def enter_a_random_room(driver, text_only)
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	room_name = wait.until { driver.find_element(:id => "ctl00_ContentPlaceHolder1_txtRoomName") }
	room_name.send_keys "test_" + Random.rand(1000).to_s()
	if ( text_only == true )
		room_select_button = driver.find_element :id => "ctl00_ContentPlaceHolder1_btnPhoneLogin"
	else
		room_select_button = driver.find_element :id => "ctl00_ContentPlaceHolder1_btnSelectRoom"
	end
	room_select_button.click
	
	sleep( 5 )
	nick_selection = wait.until { driver.find_element(:id => "ctl00_ContentPlaceHolder1_txtUserName") }
	#nick_selection.send_keys :back,:back,:back,:back,:back,:back,:back,:back,:back,:back,:back,:back,:back,:back,:back
	nick_selection.clear
	nick_selection.send_keys "testing_selenium"
	
	room_entry_button = wait.until { driver.find_element(:id => "ctl00_ContentPlaceHolder1_btnLogin") }
	room_entry_button.click
	sleep( 3 )
end
	
def leave_room(driver)
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	element = wait.until { driver.find_element(:link_text => "Leave") }
	element.click
end
	
def send_text(driver, text_field, message, wait_for )
	if  message != ""
		print "sending: " + message + "\n"
		text_field.send_keys message, :enter
		sleep(1)
	end
	if wait_for != ""
		chat_history = driver.find_element :id => "txt"
		if chat_history.text.include? wait_for
			print "  got: " + wait_for + "\n"
		else
			log_error "  did not find: " + wait_for + "\n"
		end
	end
end

def test_chat_basic(server, driver)
	print 'hello from test_chat_basic\n'
	enter_a_random_room(driver,false)
	
	wait = Selenium::WebDriver::Wait.new(:timeout => 3) # seconds
	chat_text = driver.find_element :id => "txtMsg"
	chat_btn = driver.find_element :id => "btn"
	
	send_text( driver, chat_text, "hello!", "hello!" )
	send_text( driver, chat_text, "camming up", "")

	broadcast_btn = driver.find_element :link_text => "Broadcast"
	broadcast_btn.click
	sleep(5)
	send_text( driver, chat_text, "/cam on", "")
	sleep(1)
	send_text( driver, chat_text, "", "you are broadcasting")

	send_text( driver, chat_text, "color test", "color test" )
	send_text( driver, chat_text, "/nick m0nk_3y_123", "testing_selenium is now m0nk_3y_123" )
	send_text( driver, chat_text, "hello1", "hello1" )
	send_text( driver, chat_text, "/nick m0nk_3y_123", "name is already" )
	send_text( driver, chat_text, "hello2", "hello2" )
	send_text( driver, chat_text, "/nick doodwtf_123", "m0nk_3y_123 is now doodwtf_123" )
	send_text( driver, chat_text, "hello3", "hello3" )
	send_text( driver, chat_text, "/nick digi_123", "doodwtf_123 is now digi_123" )
	send_text( driver, chat_text, "hello4", "hello4" )
	send_text( driver, chat_text, "/nick <iframe onload=\"alert('hi');\" ></iframe>", "digi_123 is now iframeonloadalerthiiframe" )
	send_text( driver, chat_text, "/nick test_123", "iframeonloadalerthiiframe is now test_123" )
	send_text( driver, chat_text, "hello5", "hello5" )
	send_text( driver, chat_text, "/help", "")
	send_text( driver, chat_text, "/oper test_123", "test_123 is already an operator" )
	send_text( driver, chat_text, "/cam refresh", "")
	send_text( driver, chat_text, "/cam down testtesttest", "does not appear to be on cam" )
	send_text( driver, chat_text, "/cam down test_123", "test_123 has cammed down test_123" )
	send_text( driver, chat_text, "/msg test_123 hello there in a whisper", "hello there in a whisper" )
	send_text( driver, chat_text, "/me is happy", "test_123 is happy" )
	send_text( driver, chat_text, "/ignore happy", "user not found" )
	send_text( driver, chat_text, "/ignore test_123", "You can't ignore yourself" )
	send_text( driver, chat_text, "you should get to work on making this http://i.imgur.com/KoyQ3.jpg the background image of the chat", "the background image of the chat" )
	send_text( driver, chat_text, "/msg test <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/kick <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/topic I like boobs", "has set the room topic to" )
	send_text( driver, chat_text, "/topic <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/privmsg test|<iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/topic this is a new topic","has set the room topic to" )
	send_text( driver, chat_text, "/clear","" )
	send_text( driver, chat_text, "/back http://www.bostonherald.com/blogs/sports/rap_sheet/wp-content/uploads/2009/08/happy_birthday_cake.jpg", "must be hosted on imgur.com to prevent IP harvesting")
	send_text( driver, chat_text, "/topic this is a <b>new</b> topic","has set the room topic to")
	send_text( driver, chat_text, "/back", "test_123 has cleared the background image" )
	send_text( driver, chat_text, "/back http://www.bostsonherald.com/blogs/sports/rap_sheet/wp-content/uploads/2009/08/happy_birthday_cake.jpg", "" )
	
	# close the PM box
	failed_pm = driver.find_element :id => "remove_test"
	failed_pm.click
	
	leave_room(driver)
end

def test_textonly(server, driver)
	print 'hello from test_textonly\n'
	enter_a_random_room(driver,true)
	
	wait = Selenium::WebDriver::Wait.new(:timeout => 3) # seconds
	chat_text = driver.find_element :id => "txtMsg"
	chat_btn = driver.find_element :id => "btn"
	
	send_text( driver, chat_text, "hello!", "hello!" )

	send_text( driver, chat_text, "color test", "color test" )
	send_text( driver, chat_text, "/nick m0nk_3y_123", "testing_selenium is now m0nk_3y_123" )
	send_text( driver, chat_text, "hello1", "hello1" )
	send_text( driver, chat_text, "/nick m0nk_3y_123", "name is already" )
	send_text( driver, chat_text, "hello2", "hello2" )
	send_text( driver, chat_text, "/nick doodwtf_123", "m0nk_3y_123 is now doodwtf_123" )
	send_text( driver, chat_text, "hello3", "hello3" )
	send_text( driver, chat_text, "/nick digi_123", "doodwtf_123 is now digi_123" )
	send_text( driver, chat_text, "hello4", "hello4" )
	send_text( driver, chat_text, "/nick <iframe onload=\"alert('hi');\" ></iframe>", "digi_123 is now iframeonloadalerthiiframe" )
	send_text( driver, chat_text, "/nick test_123", "iframeonloadalerthiiframe is now test_123" )
	send_text( driver, chat_text, "hello5", "hello5" )
	send_text( driver, chat_text, "/help", "")
	send_text( driver, chat_text, "/oper test_123", "test_123 is already an operator" )
	send_text( driver, chat_text, "/msg test_123 hello there in a whisper", "hello there in a whisper" )
	send_text( driver, chat_text, "/me is happy", "test_123 is happy" )
	send_text( driver, chat_text, "/ignore happy", "user not found" )
	send_text( driver, chat_text, "/ignore test_123", "You can't ignore yourself" )
	send_text( driver, chat_text, "you should get to work on making this http://i.imgur.com/KoyQ3.jpg the background image of the chat", "the background image of the chat" )
	send_text( driver, chat_text, "/msg test <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/kick <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/topic I like boobs", "has set the room topic to" )
	send_text( driver, chat_text, "/topic <iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/privmsg test|<iframe onload=\"alert('hi');\" ></iframe>", "" )
	send_text( driver, chat_text, "/topic this is a new topic","has set the room topic to" )
	send_text( driver, chat_text, "/clear","" )
	send_text( driver, chat_text, "/back http://www.bostonherald.com/blogs/sports/rap_sheet/wp-content/uploads/2009/08/happy_birthday_cake.jpg", "must be hosted on imgur.com to prevent IP harvesting")
	send_text( driver, chat_text, "/topic this is a <b>new</b> topic","has set the room topic to")
	send_text( driver, chat_text, "/back", "test_123 has cleared the background image" )
	send_text( driver, chat_text, "/back http://www.bostsonherald.com/blogs/sports/rap_sheet/wp-content/uploads/2009/08/happy_birthday_cake.jpg", "" )
	
	# close the PM box
	failed_pm = driver.find_element :id => "remove_test"
	failed_pm.click
	
	leave_room(driver)
end

def test_xss(server, driver)
	print 'hello from test_xss\n'
	enter_a_random_room(driver,false)
	
	wait = Selenium::WebDriver::Wait.new(:timeout => 3) # seconds
	chat_text = driver.find_element :id => "txtMsg"
	chat_btn = driver.find_element :id => "btn"
	
	send_text( driver, chat_text, "http://'style='font-size:72px;color:#fff''onmouseover='x=\"\";s=\"\\x20\";$(\"#txtMsg\"+x).val(\"I\"+s+\"have\"+s+\"crabs\"+x);button_clicked();clearChatHistory();", "" )
	send_text( driver, chat_text, "http://'style='color:#fff''onmouseover='x=\"\";r=\"click\";s=\"\\x20\";$(this).fancybox({type:\"iframe\",href:\"http://www.youtube.com/v/ZuUxI6MEILU&autoplay=1\"}).trigger(r);$(this).remove();$(\"#txtMsg\"+x).val(\"dunce\"+x);button_clicked();'", "" )
	send_text( driver, chat_text, "http://'style='color:#fff''onmouseover='x=\"\";r=\"click\";s=\"\\x20\";$(this).fancybox({type:\"iframe\",href:\"http://www.youtube.com/v/qrBj3u5dPgM&autoplay=1\"}).trigger(r);$(this).remove();$(\"#txtMsg\"+x).val(\"x\"+x);button_clicked();'", "" )
	send_text( driver, chat_text, "http://'style='color:#fff''onmouseover='x=\"\";r=\"click\";s=\"\\x20\";$(this).fancybox({type:\"iframe\",href:\"http://www.youtube.com/v/4s0_QKL3O8s&autoplay=1\"}).trigger(r);$(this).remove();$(\"#txtMsg\"+x).val(\"x\"+x);button_clicked();'", "" )
	send_text( driver, chat_text, "http://'style='color:#fff''onmouseover='x=\"\";s=\"\\x20\";grr=$(\"div>b>a>img\"+x);grr.attr({src:\"http://i.imgur.com/CfBsF.gif\"});grr.animate({marginLeft:\"-=600px\"},13000);$(this).remove();'", "" )
	send_text( driver, chat_text, "http://'style='color:#ff''onmouseover='x=\"\";s=\"\\x20\";grr=$(\"div>b>a>img\"+x);grr.prependTo(\"body\"+x).attr({src:\"http://i.imgur.com/CfBsF.gif\"}).animate({marginLeft:\"+=600px\",height:\"200px\",width:\"200px\"},13000);$(this).remove();'", "" )
	send_text( driver, chat_text, "i iz dun h4x0ring... this one should come through as a valid hot link for other chatters in this room", "" )
	send_text( driver, chat_text, "http://www.americhip.com/cgi-bin/complex2/proddisp.pl?client=americhip&catid=17&PRID=109", "" )
	
	leave_room(driver)
end
	
def try_onetime_payment( driver, amount, card, cvc, month, year, zip_code, expected_response)
	driver.get "http://localhost/gethearted"
	amt = driver.find_element :class => "payment-amt"
	amt.clear
	amt.send_keys amount

	driver.find_element(:class => "card-number-onetime").send_keys card
	driver.find_element(:class => "card-cvc-onetime").send_keys cvc
	driver.find_element(:class => "card-expiry-month-onetime").send_keys month
	driver.find_element(:class => "card-expiry-year-onetime").send_keys year
	driver.find_element(:class => "billing-zip-code-onetime").send_keys zip_code, :enter
	sleep 4
	
	if !driver.page_source.include?(expected_response) then
		print "Submitting one time payment failed to get expected response [" + expected_response + "]\n"
		sleep 3
		if !driver.page_source.include?(expected_response) then
			print "Submitting one time payment failed to get expected response [" + expected_response + "]\n"
			crash_here
		end
	end
end

def try_monthly_payment( driver, amount, card, cvc, month, year, zip_code, expected_response)
	driver.get "http://localhost/gethearted"
	amt = driver.find_element :class => "payment-amt"
	amt.clear
	amt.send_keys amount

	driver.find_element(:class => "card-number").send_keys card
	driver.find_element(:class => "card-cvc").send_keys cvc
	driver.find_element(:class => "card-expiry-month").send_keys month
	driver.find_element(:class => "card-expiry-year").send_keys year
	driver.find_element(:class => "billing-zip-code").send_keys zip_code, :enter
	sleep 4
	
	if !driver.page_source.include?(expected_response) then
		print "Submitting monthly payment failed to get expected response [" + expected_response + "]\n"
		sleep 3
		if !driver.page_source.include?(expected_response) then
			print "Submitting monthly payment failed to get expected response [" + expected_response + "]\n"
			crash_here
		end
	end
end
	
def get_pop_up_window(driver)
	begin
		alert = driver.switch_to.alert
	rescue
		sleep 2
		begin
			alert = driver.switch_to.alert
		rescue
			sleep 2
			alert = driver.switch_to.alert
		end
	end
	alert
	end
	
def try_monthly_payment_failure( driver, amount, card, cvc, month, year, zip_code, expected_response)
	driver.get "http://localhost/gethearted"
	amt = driver.find_element :class => "payment-amt"
	amt.clear
	amt.send_keys amount

	driver.find_element(:class => "card-number").send_keys card
	driver.find_element(:class => "card-cvc").send_keys cvc
	driver.find_element(:class => "card-expiry-month").send_keys month
	driver.find_element(:class => "card-expiry-year").send_keys year
	driver.find_element(:class => "billing-zip-code").send_keys zip_code, :enter
	sleep 2

	alert = get_pop_up_window( driver )
	alert_text = alert.text
	alert.accept
	
	if !alert_text.include?(expected_response) then
		print "Submitting monthly payment failed to get expected ERROR response [" + expected_response + "], got [" + alert_text + "] instead\n"
		crash_here
	end
end

def test_payments(server, driver)

	## BUGBUG - need to pick the right date from the combo box / select	
	try_monthly_payment( driver, "12.99", "4242424242424242", "1234", "12", "2014", "10467", "Thank you!" )
	try_onetime_payment( driver, "3.00", "4242424242424242", "1234", "12", "2014", "10467", "Thank you!" )

	## try the error cases
	try_monthly_payment_failure( driver, "12.99", "4242424242424242", "1234", "12", "2010", "10467", "Your card's expiration year is invalid" ) # wrong year
	try_monthly_payment_failure( driver, "12.99", "4242424242424242", "1234", "13", "2014", "10467", "Your card's expiration month is invalid" ) # wrong month
	try_monthly_payment_failure( driver, "12.99", "4242424242424242", "12", "12", "2014", "10467", "Your card's security code is invalid" ) # wrong crc
	try_monthly_payment_failure( driver, "12.99", "4242424242424241", "1234", "12", "2014", "10467", "Your card number is incorrect" ) # wrong card
	
	# mark the messages as unread (so they don't pop-up in chat)
	driver.find_element(:link, "messages").click
end
	
def send_message(server, driver, account, message)
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	acc = text_from_file(account).split(":")[0]
	driver.get server + "/user/" + acc

	#this sucks, there must be a better way to work around these lameass generated id's
	send_message_btn = wait.until { driver.find_element :id => "ctl00_ContentPlaceHolder1_btnSendMessage" }
	send_message_btn.click

	element = wait.until { driver.find_element :id => "ctl00_ContentPlaceHolder1_txtNewMessage" }
	element.send_keys message

	send_message_btn = wait.until { driver.find_element :id => "ctl00_ContentPlaceHolder1_btnSendMessage" }
	send_message_btn.click

	wait.until { driver.title.downcase.start_with? "messages" }
end
	
def check_message_thread(server, driver, account, message)
	acc = text_from_file(account).split(":")[0]
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	wait.until { driver.title.downcase.start_with? "icanhazchat" }

	# do we have unread messages? BUGGY, DOESNT ALWAYS EXIST!
	begin
		element = driver.find_element :id => "unread_mess"
	rescue
		log_error("no unread messages")
	end
	
	element = wait.until { driver.find_element(:link_text => "messages") }
	element.click	
	
	driver.execute_script("open_message('" + acc + "')")
	wait.until { driver.title.downcase.start_with? acc }

	if (!driver.page_source.include?(message)) 
		log_error("message not delivered")
	end
end	
	
def delete_message_thread(server, driver, account)
	wait = Selenium::WebDriver::Wait.new(:timeout => 10) # seconds
	acc = text_from_file(account).split(":")[0]
	driver.get server + "/messages/" + acc
	
	#tried to use alt-x before, but then switching to alert fails. 
	element = wait.until { driver.find_element(:id => "ctl00_ContentPlaceHolder1_btnDeleteThread") }
	element.click()
	alert = get_pop_up_window(driver)
	if !alert.text.include?("Delete this message thread?") then
		log_error("Pop up alert when deleting messages contained unexpected text [" + alert.text + "]")
	end
	alert.accept
end

def test_offline_messaging(server, driver, account1, account2)
	message_text = "random text " + Random.rand(10000).to_s()
	sign_into_site(server, driver, account1)
	send_message(server, driver, account2, message_text)
	sign_out_of_site(server,driver)
	sign_into_site(server, driver, account2)
	check_message_thread(server, driver, account1, message_text)
	delete_message_thread(server, driver, account1)
end
	
def log_error(msg)
	@error_log << msg
	print msg + "\n"
	# raise msg
	sleep(1)
end
	
#drivers_to_use = Array['chrome', 'firefox', 'ie']
#drivers_to_use = Array['chrome', 'ie']
drivers_to_use = Array['chrome','firefox']
#drivers_to_use = Array['ie', 'chrome','firefox']
#drivers_to_use = Array['opera']

#drivers_to_use = Array['chrome']
#drivers_to_use = Array['firefox']
#drivers_to_use = Array['ie']

#print "defining"
#tests_to_run = Array[test_chat_basic, test_textonly, test_xss]
#print "done defining"

@error_log = []
server = text_from_file("server_to_test.txt")

drivers_to_use.each { |driver_name|
	# set_configuration( "localhost", driver );
	
	case driver_name
		when "chrome"
			driver = Selenium::WebDriver.for :chrome
		when "ie"
			driver = Selenium::WebDriver.for :ie
		when "opera"
			driver = Selenium::WebDriver.for :opera
		else
			driver = Selenium::WebDriver.for :firefox
	end

	account1 = "account1.txt"
	account2 = "account2.txt"

	test_offline_messaging(server, driver, account1, account2)
	sign_out_of_site(server, driver)

	sign_into_site(server, driver, account1)
	test_chat_basic(server, driver)
	sign_out_of_site(server, driver)

	sign_into_site(server, driver, account1)
	test_textonly(server, driver)
	sign_out_of_site(server, driver)
	sign_into_site(server, driver, account1)

	test_xss(server, driver)
	sign_out_of_site(server, driver)

	sign_into_site(server, driver, account1)
	if server.include?("localhost") && driver_name != "ie" then
		test_payments(server, driver)
		driver.get server
	end

	print "Errors found: " + @error_log.size().to_s + "\n"
	@error_log.each { |msg|
		print "FOUND ERROR: " + msg + "\n"
	}

	# chromedriver.exe sometimes crashes here - their bug?
	driver.quit
}

